package com.jiesen.base.baseDao;


import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.jiesen.base.utils.ObjectToMap;


/**
 * <p>
 * 基础Service实现 继承于Mybatis-plus
 * </p>
 *
 * @author Caratacus
 */
@Transactional(readOnly = true)
public class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> {

	@Autowired
	protected M baseMapper;

	/**
	 * <p>
	 * 判断数据库操作是否成功
	 * </p>
	 *
	 * @param result 数据库操作返回影响条数
	 * @return boolean
	 */
	protected boolean retBool(Integer result) {
		return SqlHelper.retBool(result);
	}

	protected Class<T> currentModelClass() {
		return ReflectionKit.getSuperClassGenericType(getClass(), 1);
	}

	/**
	 * <p>
	 * 批量操作 SqlSession
	 * </p>
	 */
	protected SqlSession sqlSessionBatch() {
		return SqlHelper.sqlSessionBatch(currentModelClass());
	}

	/**
	 * 释放sqlSession
	 *
	 * @param sqlSession session
	 */
	protected void closeSqlSession(SqlSession sqlSession) {
		SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
	}

	/**
	 * 获取SqlStatement
	 *
	 * @param sqlMethod
	 * @return
	 */
	protected String sqlStatement(SqlMethod sqlMethod) {
		return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean save(T entity) {
		return retBool(baseMapper.insert(entity));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void saveBatch(Collection<T> entityList) {
		if (CollectionUtils.isEmpty(entityList)) {
			return;
		}
		baseMapper.insertBatchSomeColumn(entityList);
	}

	/**
	 * <p>
	 * TableId 注解存在更新记录，否插入一条记录
	 * </p>
	 *
	 * @param entity 实体对象
	 * @return boolean
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean saveOrUpdate(T entity) {
		if (null != entity) {
			Class<?> cls = entity.getClass();
			TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
			if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
				Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
				if (StringUtils.checkValNull(idVal)) {
					return save(entity);
				} else {
					/*
					 * 更新成功直接返回，失败执行插入逻辑
					 */
					return Objects.nonNull(getById((Serializable) idVal)) ? updateById(entity) : save(entity);
				}
			} else {
				throw ExceptionUtils.mpe("Error:  Can not execute. Could not find @TableId.");
			}
		}
		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean saveOrUpdateBatch(Collection<T> entityList) {
		//批量对象插入 不存在直接返回true
		if (CollectionUtils.isEmpty(entityList)) {
			return true;
		}
		Class<?> cls = currentModelClass();
		TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
		int i = 0;
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			for (T entity : entityList) {
				if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
					Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
					if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {
						batchSqlSession.insert(sqlStatement(SqlMethod.INSERT_ONE), entity);
					} else {
						MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
						param.put(Constants.ENTITY, entity);
						batchSqlSession.update(sqlStatement(SqlMethod.UPDATE_BY_ID), param);
					}
					//不知道以后会不会有人说更新失败了还要执行插入 😂😂😂
					if (i >= 1 && i % batchSize == 0) {
						batchSqlSession.flushStatements();
					}
					i++;
				} else {
					throw ExceptionUtils.mpe("Error:  Can not execute. Could not find @TableId.");
				}
			}
			batchSqlSession.flushStatements();
		}
		return true;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean removeById(Serializable id) {
		return SqlHelper.delBool(baseMapper.deleteById(id));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean remove(Wrapper<T> queryWrapper) {
		return SqlHelper.delBool(baseMapper.delete(queryWrapper));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean updateById(T entity) {
		return retBool(baseMapper.updateById(entity));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean updateAllColumnById(T entity) {
		return retBool(baseMapper.updateAllColumnById(entity));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean update(T entity, Wrapper<T> updateWrapper) {
		return retBool(baseMapper.update(entity, updateWrapper));
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public boolean updateBatchById(Collection<T> entityList, int batchSize) {
		//批量对象插入 不存在直接返回true
		if (CollectionUtils.isEmpty(entityList)) {
			return true;
		}
		int i = 0;
		String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			for (T anEntityList : entityList) {
				MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
				param.put(Constants.ENTITY, anEntityList);
				batchSqlSession.update(sqlStatement, param);
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
				i++;
			}
			batchSqlSession.flushStatements();
		}
		return true;
	}

	@Override
	public T getById(Serializable id) {
		return baseMapper.selectById(id);
	}

	@Override
	public int count(Wrapper<T> queryWrapper) {
		return SqlHelper.retCount(baseMapper.selectCount(queryWrapper));
	}

	@Override
	public List<T> list(Wrapper<T> queryWrapper) {
		return baseMapper.selectList(queryWrapper);
	}

	@Override
	public IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper) {
		return baseMapper.selectPage(page, queryWrapper);
	}

	//bao
	@Override
	public com.jiesen.base.utils.R page(T t) {
		HashMap<String,Object> map = ObjectToMap.to(t);
		IPage<T> page = new Page<>(Integer.parseInt(map.get("page").toString()),Integer.parseInt(map.get("limit").toString()));
		QueryWrapper<T> queryWrapper =  new QueryWrapper<>();
		for (Map.Entry<String, Object> m : map.entrySet()) {

			//System.out.println("key:" + m.getKey() + " value:" + m.getValue());
			if(m.getKey().equals("totalSize")||m.getKey().equals("limit")||m.getKey().equals("start")||m.getKey().equals("page")||m.getKey().equals("pageid")||m.getKey().equals("totalCount")) continue;

			String[] split = m.getValue().toString().split("_");
			if(split[0].equals("like")) {
				//驼峰转下划线
				queryWrapper.like(ObjectToMap.humpToLine2(m.getKey()) , split[1]);
			}else {
				queryWrapper.eq(ObjectToMap.humpToLine2(m.getKey()), m.getValue());
			}

		}
		IPage<T> selectPage = baseMapper.selectPage(page, queryWrapper);

		return com.jiesen.base.utils.R.ok().put("data", selectPage.getRecords()).put("total", selectPage.getTotal());
	}

	//bao
		@Override
		public IPage<T> page1(T t) {
			HashMap<String,Object> map = ObjectToMap.to(t);
			//IPage<T> page = new Page<>(Integer.parseInt(map.get("page").toString()),Integer.parseInt(map.get("limit").toString()));
			IPage<T> page = new Page<>(Integer.parseInt(map.get("page").toString()),Integer.parseInt(map.get("limit").toString()));
			QueryWrapper<T> queryWrapper =  new QueryWrapper<>();
			for (Map.Entry<String, Object> m : map.entrySet()) {

				System.out.println("key:" + m.getKey() + " value:" + m.getValue());
				if(m.getKey().equals("totalSize")||m.getKey().equals("limit")||m.getKey().equals("start")||m.getKey().equals("page")||m.getKey().equals("pageid")||m.getKey().equals("totalCount")) continue;

				String[] split = m.getValue().toString().split("_");
				if(split[0].equals("like")) {
					//驼峰转下划线
					queryWrapper.like(ObjectToMap.humpToLine2(m.getKey()) , split[1]);
				}else {
					queryWrapper.eq(ObjectToMap.humpToLine2(m.getKey()), m.getValue());
				}

			}
			IPage<T> selectPage = baseMapper.selectPage(page, queryWrapper);

			return selectPage;
		}

    @Override
    public List<T> list(List ids) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);
        return baseMapper.selectList(queryWrapper);
    }

    //bao
	@Override
	public com.jiesen.base.utils.R list(T t) {
		HashMap<String,Object> map = ObjectToMap.to(t);
		QueryWrapper<T> queryWrapper =  new QueryWrapper<>();
		for (Map.Entry<String, Object> m : map.entrySet()) {

			System.out.println("key:" + m.getKey() + " value:" + m.getValue());
			if(m.getKey().equals("totalSize")||m.getKey().equals("limit")||m.getKey().equals("start")||m.getKey().equals("page")||m.getKey().equals("pageid")||m.getKey().equals("totalCount")) continue;

			String[] split = m.getValue().toString().split("_");
			if(split[0].equals("like")) {
				//驼峰转下划线
				queryWrapper.like(ObjectToMap.humpToLine2(m.getKey()) , split[1]);
			}else if(split[0].equals("asc")){
				queryWrapper.orderByAsc(ObjectToMap.humpToLine2(m.getKey()));
			}else if(split[0].equals("desc")){
				queryWrapper.orderByDesc(ObjectToMap.humpToLine2(m.getKey()));
			}else {
				queryWrapper.eq(ObjectToMap.humpToLine2(m.getKey()), m.getValue());
			}

		}
		List<T> list = baseMapper.selectList(queryWrapper);

		return com.jiesen.base.utils.R.ok().put("data", list).put("total", list.size());
	}

	@Override
	public List<T> list1(T t) {
		HashMap<String,Object> map = ObjectToMap.to(t);
		QueryWrapper<T> queryWrapper =  new QueryWrapper<>();
		for (Map.Entry<String, Object> m : map.entrySet()) {

			System.out.println("key:" + m.getKey() + " value:" + m.getValue());
			if(m.getKey().equals("totalSize")||m.getKey().equals("limit")||m.getKey().equals("start")||m.getKey().equals("page")||m.getKey().equals("pageid")||m.getKey().equals("totalCount")) continue;

			String[] split = m.getValue().toString().split("_");
			if(split[0].equals("like")) {
				//驼峰转下划线
				queryWrapper.like(ObjectToMap.humpToLine2(m.getKey()) , split[1]);
			}else if(split[0].equals("asc")){
				queryWrapper.orderByAsc(ObjectToMap.humpToLine2(m.getKey()));
			}else if(split[0].equals("desc")){
				queryWrapper.orderByDesc(ObjectToMap.humpToLine2(m.getKey()));
			}else {
				queryWrapper.eq(ObjectToMap.humpToLine2(m.getKey()), m.getValue());
			}

		}
		List<T> list = baseMapper.selectList(queryWrapper);

		return list;
	}



	@Override
	public <R> List<R> listObjs(Wrapper<T> queryWrapper, Function<? super Object, R> mapper) {
		return baseMapper.selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
	}

	@Override
	public <R> IPage<R> pageEntities(IPage page, Wrapper<T> wrapper, Function<? super T, R> mapper) {
		return page(page, wrapper).convert(mapper);
	}

	@Override
	public <R> List<R> entitys(Wrapper<T> wrapper, Function<? super T, R> mapper) {
		return list(wrapper).stream().map(mapper).collect(Collectors.toList());
	}

	private <K> Map<K, T> list2Map(List<T> list, SFunction<T, K> column) {
		if (list == null) {
			return Collections.emptyMap();
		}
		Map<K, T> map = new LinkedHashMap<>(list.size());
		for (T t : list) {
			Field field = ReflectionUtils.findField(t.getClass(), getColumn(LambdaUtils.resolve(column)));
			if (Objects.isNull(field)) {
				continue;
			}
			ReflectionUtils.makeAccessible(field);
			Object fieldValue = ReflectionUtils.getField(field, t);
			map.put((K) fieldValue, t);
		}
		return map;
	}

	@Override
	public <K> Map<K, T> list2Map(Wrapper<T> wrapper, SFunction<T, K> column) {
		return list2Map(list(wrapper), column);
	}

	private String getColumn(SerializedLambda lambda) {
		return StringUtils.resolveFieldName(lambda.getImplMethodName());
	}

}
